1 #include <CoreFoundation/CoreFoundation.h>
2 #include <CoreServices/CoreServices.h>
3 #import "GetMetadataForHTMLLog.h"
4 #import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
7 Relevant keys from MDItem.h we use or may want to use:
9 @constant kMDItemContentCreationDate
10 This is the date that the contents of the file were created,
11 has an application specific semantic.
14 @constant kMDItemTextContent
15 Contains the text content of the document. Type is a CFString.
17 @constant kMDItemDisplayName
18 This is the localized version of the LaunchServices call
19 LSCopyDisplayNameForURL()/LSCopyDisplayNameForRef().
21 @const kMDItemInstantMessageAddresses
22 Instant message addresses for this item. Type is an Array of CFStrings.
25 /* -----------------------------------------------------------------------------
26 Get metadata attributes from file
28 This function's job is to extract useful information your file format supports
29 and return it as a dictionary
30 ----------------------------------------------------------------------------- */
32 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile);
33 NSString *GetTextContentForXMLLog(NSString *pathToFile);
35 Boolean GetMetadataForFile(void* thisInterface,
36 CFMutableDictionaryRef attributes,
37 CFStringRef contentTypeUTI,
38 CFStringRef pathToFile)
40 /* Pull any available metadata from the file at the specified path */
41 /* Return the attribute keys and attribute values in the dict */
42 /* Return TRUE if successful, FALSE if there was no data provided */
44 Boolean success = FALSE;
45 NSAutoreleasePool *pool;
46 pool = [[NSAutoreleasePool alloc] init];
48 if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.htmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
49 success = GetMetadataForHTMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
50 } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
51 success = GetMetadataForXMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
53 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
62 * @brief Copy the text content for a file
64 * This is the text which would be the kMDItemTextContent for the file in Spotlight.
66 * @param contentTypeUTI The UTI type. If NULL, the extension of pathToFile will be used
67 * @param pathToFile The full path to the file
69 * @result The kMDItemTextContent. Follows the Copy rule.
71 CFStringRef CopyTextContentForFile(CFStringRef contentTypeUTI,
72 CFStringRef pathToFile)
74 NSAutoreleasePool *pool;
75 CFStringRef textContent;
76 pool = [[NSAutoreleasePool alloc] init];
78 //Deteremine the UTI type if we weren't passed one
79 if (contentTypeUTI == NULL) {
80 if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
82 (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
83 contentTypeUTI = CFSTR("com.adiumx.xmllog");
84 } else if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
86 (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
87 contentTypeUTI = CFSTR("com.adiumx.xmllog");
89 //Treat all other log extensions as HTML logs (plaintext will come out fine this way, too)
90 contentTypeUTI = CFSTR("com.adiumx.htmllog");
94 if (CFStringCompare(contentTypeUTI, CFSTR("com.adiumx.htmllog"), kCFCompareBackwards) == kCFCompareEqualTo) {
95 textContent = (CFStringRef)GetTextContentForHTMLLog((NSString *)pathToFile);
96 } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
97 textContent = (CFStringRef)GetTextContentForXMLLog((NSString *)pathToFile);
101 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
104 if (textContent) CFRetain(textContent);
111 * @brief get metadata for an XML file
113 * This function gets the metadata contained within a universal chat log format file
114 * @param attributes The dictionary in which to store the metadata
115 * @param pathToFile The path to the file to index
117 * @result true if successful, false if not
119 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile)
122 NSXMLDocument *xmlDoc;
124 NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
125 xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
126 options:NSXMLNodePreserveCDATA
131 NSArray *senderNodes = [xmlDoc nodesForXPath:@"//message/@sender"
133 NSSet *duplicatesRemover = [NSSet setWithArray: senderNodes];
134 // XPath returns an array of NSXMLNodes. Must convert them to strings containing just the attribute value.
135 NSMutableArray *authorsArray = [NSMutableArray arrayWithCapacity:[duplicatesRemover count]];
136 NSEnumerator *enumerator = [duplicatesRemover objectEnumerator];
137 NSXMLNode *senderNode = nil;
139 while( ( senderNode = [enumerator nextObject] ) ) {
140 [authorsArray addObject:[senderNode objectValue]];
143 [(NSMutableDictionary *)attributes setObject:authorsArray
144 forKey:(NSString *)kMDItemAuthors];
146 [(NSMutableDictionary *)attributes setObject:authorsArray
147 forKey:(NSString *)kMDItemInstantMessageAddresses];
149 NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
151 NSString *contentString = [contentArray componentsJoinedByString:@" "];
153 [attributes setObject:contentString
154 forKey:(NSString *)kMDItemTextContent];
156 NSString *serviceString = [[[xmlDoc rootElement] attributeForName:@"service"] objectValue];
157 if(serviceString != nil)
158 [attributes setObject:serviceString
159 forKey:@"com_adiumX_service"];
161 NSArray *children = [[xmlDoc rootElement] children];
162 NSCalendarDate *startDate = nil, *endDate = nil;
164 if ([children count]) {
167 dateStr = [[(NSXMLElement *)[children objectAtIndex:0] attributeForName:@"time"] objectValue];
168 startDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
170 [(NSMutableDictionary *)attributes setObject:startDate
171 forKey:(NSString *)kMDItemContentCreationDate];
173 dateStr = [[(NSXMLElement *)[children lastObject] attributeForName:@"time"] objectValue];
174 endDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
176 [(NSMutableDictionary *)attributes setObject:[NSNumber numberWithInt:[endDate timeIntervalSinceDate:startDate]]
177 forKey:(NSString *)kMDItemDurationSeconds];
180 NSString *accountString = [[[xmlDoc rootElement] attributeForName:@"account"] objectValue];
182 [attributes setObject:accountString
183 forKey:@"com_adiumX_chatSource"];
184 NSMutableArray *otherAuthors = [authorsArray mutableCopy];
185 [otherAuthors removeObject:accountString];
186 [attributes setObject:otherAuthors
187 forKey:@"com_adiumX_chatDestinations"];
188 //pick the first author for this. likely a bad idea
189 if (startDate && [otherAuthors count]) {
190 NSString *toUID = [otherAuthors objectAtIndex:0];
191 [attributes setObject:[NSString stringWithFormat:@"%@ on %@",toUID,[startDate descriptionWithCalendarFormat:@"%Y-%m-%d"
194 forKey:(NSString *)kMDItemDisplayName];
196 [otherAuthors release];
199 [attributes setObject:@"Chat log"
200 forKey:(NSString *)kMDItemKind];
201 [attributes setObject:@"Adium"
202 forKey:(NSString *)kMDItemCreator];
212 NSString *killXMLTags(NSString *inString)
214 NSScanner *scan = [NSScanner scannerWithString:inString];
215 NSString *tempString = nil;
216 NSMutableString *ret = [NSMutableString string];
219 while(![scan isAtEnd]){
221 [scan scanUpToString:@"<" intoString:&tempString];
222 if(tempString != nil)
223 [ret appendFormat:@"%@ ", tempString];
224 [scan scanString:@"<" intoString:nil];
225 [scan scanUpToString:@">" intoString:&tempString];
226 if([tempString hasPrefix:@"br"])
227 [ret appendString:@"\n"];
228 [scan scanString:@">" intoString:nil];
232 NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
233 rng = [ret rangeOfString:@"<" options:0 range:searchRange];
235 [ret replaceCharactersInRange: rng withString: @"<"];
236 } while (rng.length > 0);
239 NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
240 rng = [ret rangeOfString:@">" options:0 range:searchRange];
242 [ret replaceCharactersInRange: rng withString: @">"];
243 } while (rng.length > 0);
246 NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
247 rng = [ret rangeOfString:@"&" options:0 range:searchRange];
249 [ret replaceCharactersInRange: rng withString: @"&"];
250 } while (rng.length > 0);
254 NSString *GetTextContentForXMLLog(NSString *pathToFile)
257 NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
258 NSString *contentString;
259 NSXMLDocument *xmlDoc;
260 xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
261 options:NSXMLNodePreserveCDATA
263 NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
265 contentString = [contentArray componentsJoinedByString:@" "];
267 return contentString;